package soeasy.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;

import umontreal.iro.lecuyer.randvar.RandomVariateGen;

public class ISIGenerator {

	private static double defaultMinimumISI = 20.0;

	private static double defaultMaximumISI = 200.0;

	protected DoubleArrayList isiArray;

	private double minimumISI = 20.0; // the least acceptable ISI in ms

	private double maximumISI = 200; // the most acceptable ISI in ms

	protected RandomVariateGen randomGen = null;

	/**
	 * Default empty constructor
	 */
	protected ISIGenerator() {

	}

	/**
	 * Initializes the interspike interval distribution.
	 * 
	 * @param neuronDischargeFileName
	 */
	public ISIGenerator(String neuronDischargeFileName)
			throws FileNotFoundException, IOException {
		DoubleArrayList motoneuronDischarges = new DoubleArrayList(
				neuronDischargeFileName);
		this.isiArray = calculateISI(motoneuronDischarges);		
		isiArray.quickSort();
		this.minimumISI = isiArray.get(0);
		this.maximumISI = isiArray.get(isiArray.size() - 1);
		this.randomGen = RandomVariateGenFactory.getGenerator(isiArray);
	}

	public ISIGenerator(String triggerFileName, String motorBehaviorFileName)
			throws FileNotFoundException, IOException {
		DoubleArrayList triggers = new DoubleArrayList(triggerFileName);
		DoubleArrayList motoneuronDischarges = new DoubleArrayList(
				motorBehaviorFileName);
		isiArray = calculateISI(triggers, motoneuronDischarges);
		isiArray.quickSort();
		this.minimumISI = isiArray.get(0);
		this.maximumISI = isiArray.get(isiArray.size() - 1);
		randomGen = RandomVariateGenFactory.getGenerator(isiArray);
	}

	public RandomVariateGen getRandomGen() {
		return randomGen;
	}

	public DoubleArrayList getIsiArray() {
		return this.isiArray;
	}

	public double getMinimumISI() {
		return minimumISI;
	}

	public void setMinimumISI(double minimumISI) {
		this.minimumISI = minimumISI;
	}

	public double getMaximumISI() {
		return maximumISI;
	}

	public void setMaximumISI(double maximumISI) {
		this.maximumISI = maximumISI;
	}

	public DoubleArrayList getIsiArrayList() {
		return this.isiArray;
	}

	protected DoubleArrayList calculateISI(DoubleArrayList motoneuronDischarges) {
		DoubleArrayList tempISIArray = new DoubleArrayList();
		for (int i = 1; i < motoneuronDischarges.size(); i++) {
			double isi = motoneuronDischarges.get(i)
					- motoneuronDischarges.get(i - 1);
			if ((isi >= minimumISI) && (isi <= maximumISI)) {
				tempISIArray.add(isi);
			}
		}

		// now remove unnecessary data (in this case 0)
//		double[] isiArray = new double[k];
//		int index = 0;
//		for (int i = 0; i < tempISIArray.length; i++) {
//			if (tempISIArray[i] > 0.0) {
//				isiArray[index++] = tempISIArray[i];
//			}
//		}
		return tempISIArray; //isiArray;
	}

	private DoubleArrayList calculateISI(DoubleArrayList triggers,
			DoubleArrayList motoneuronDischarges) {
		// first remove the motoneuron discharges that occur until 200 ms after
		// of the corresponding trigger.
		DoubleArrayList dischargesToRemove = new DoubleArrayList();

		for (int i = 0; i < triggers.size(); i++) {
			Double trigger = triggers.get(i);
			for (int j = 0; j < motoneuronDischarges.size(); j++) {
				Double discharge = motoneuronDischarges.get(j);
				if ((discharge >= trigger) && (discharge < trigger + 200)) {
					dischargesToRemove.add(discharge);
				}
			}
		}
		motoneuronDischarges.removeAll(dischargesToRemove);
		//

		// then calculate ISI as normal
		double[] tempISIArray = new double[motoneuronDischarges.size() - 1];
		for (int i = 1; i < motoneuronDischarges.size(); i++) {
			double isi = motoneuronDischarges.get(i)
					- motoneuronDischarges.get(i - 1);
			if ((isi >= minimumISI) && (isi <= maximumISI)) {
				tempISIArray[i - 1] = isi;
			}
		}

		// now remove unnecessary data (in this case 0)
		DoubleArrayList isiArray = new DoubleArrayList();
		for (int i = 0; i < tempISIArray.length; i++) {
			if (tempISIArray[i] > 0.0) {
				isiArray.add(tempISIArray[i]);
			}
		}

		return isiArray;
	}

	public static DoubleArrayList calculateInterspikeInterval(
			DoubleArrayList signalVector) {
		DoubleArrayList tempISIArray = new DoubleArrayList();
		for (int i = 1; i < signalVector.size(); i++) {
			double isi = signalVector.get(i) - signalVector.get(i - 1);
			if (isi >= defaultMinimumISI && isi <= defaultMaximumISI) {
				tempISIArray.add(isi);
			}
		}

		// now remove unnecessary data (in this case 0)
		DoubleArrayList isiArray = new DoubleArrayList();
		for (int i = 0; i < tempISIArray.size(); i++) {
			if (tempISIArray.get(i) > 0.0) {
				isiArray.add(tempISIArray.get(i));
			}
		}

		return isiArray;
	}

	public Vector<Double> calculateInterspikeInterval(
			Vector<Double> signalVector, double minValue, double maxValue) {
		Vector<Double> tempISIArray = new Vector<Double>();
		for (int i = 1; i < signalVector.size(); i++) {
			double isi = signalVector.get(i) - signalVector.get(i - 1);
			if (isi >= minValue && isi <= maxValue) {
				tempISIArray.add(isi);
			}
		}

		// now remove unnecessary data (in this case 0)
		Vector<Double> isiArray = new Vector<Double>();
		for (int i = 0; i < tempISIArray.size(); i++) {
			if (tempISIArray.get(i) > 0.0) {
				isiArray.add(tempISIArray.get(i));
			}
		}

		return isiArray;
	}

	/**
	 * Returns the next interspike interval. Used by MotorNeuron.
	 * 
	 * @return
	 */
	public double nextInterspikeInterval() {
		double nextDouble = 0.0;
		do {
			nextDouble = this.randomGen.nextDouble();
		} while (!((nextDouble >= minimumISI) && (nextDouble < maximumISI)));
		//
		return nextDouble; // - Spike.AMPLITUDE;
	}
}
